package maple.core.gateways;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.lang.InterruptedException;
import java.lang.Process;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jade.domain.DFService;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAException;

import maple.core.Gateway;
import maple.lang.Command;
import maple.lang.Language;
import maple.util.Profile;

public class EZPowerMeter extends Gateway
{
	private final int WAIT_TIME = 10;
	private final int SIZE = 102;
	private final int MIN_TYPE = 1;
	private final int MAX_TYPE = 4;
	private final int NUM_PORT = 11;
	
	private String _pattern;
	
	public EZPowerMeter()
	{
		super( 3 );

		_pattern = ":(\\d*.\\d*(?:,\\d*\\.\\d*){" + ( NUM_PORT - 1 ) + "})";
	}

	protected void setup()
	{
		super.setup();

		try
		{
			Command.Message message = Command.Message.ERROR;
			if( load() == Command.Message.OK )
			{
				message = modify();
			}
			if( message != Command.Message.OK )
			{
				doDelete();
			}

			// set the device
			String[] command = { "/bin/sh", "-c", "stty 38400 rows 0 columns 0 line 0 -parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8 -opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 -isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke < /dev/" + _arguments[ 1 ] };
			if( Runtime.getRuntime().exec( command ).waitFor() == 0 )
			{
				_pattern = "^(0x" + _arguments[ 2 ] + "\\d{2})" + _pattern;
			}
			else
			{
				doDelete();
			}
		}
		catch( IOException e )
		{
			_logger.log( Level.WARNING, "", e );
			doDelete();
		}
		catch( InterruptedException e )
		{
			_logger.log( Level.WARNING, "", e );
			doDelete();
		}
	}

	/**
	 * return true if adding the specified port with the specified sensitivity successful
	 *
	 * @param   port		the specified port
	 * @param   sensitivity the specified sensitivity
	 * @return  true if adding the specified port with the specified sensitivity successful
	 *
	 * @author  snowfoxy
	 * @since   2011-11-22
	 */
	protected boolean insertPort( final String port, final String sensitivity )
	{
		if( splitPort( port ) != null )
		{
			return true;
		}
		return false;
	}

	/**
	 * return the status of modifying the services
	 *
	 * @return	the status of modifying the services
	 *
	 * @author	snowfoxy
	 * @since	2011-11-22
	 */
	protected Command.Message modify()
	{
		try
		{
			Command.Message message = save();
			if( message == Command.Message.OK )
			{
				DFAgentDescription agent = getInstance( _names, _types, null, null, null );
				for( String port : _sensors.keySet() )
				{
					Profile profile = _sensors.get( port );
					agent.addServices( profile.toService( getName(), Language.Instruction.READ, port ) );
				}
				DFService.modify( this, agent );
				return Command.Message.OK;
			}
			return message;
		}
		catch( FIPAException e )
		{
			_logger.log( Level.WARNING, "", e );
			return Command.Message.EXCEPTION_FIPA;
		}
	}

	/**
	 * return the value of the specified port
	 *
	 * @param	port	the specified port
	 * @return	the value of the specified port
	 *
	 * @author	snowfoxy
	 * @since	2011-11-04
	 */
	protected double read( final String port )
	{
		double result = -1;
		try
		{
			String[] tokens = splitPort( port );
			if( tokens != null )
			{
				Process process = Runtime.getRuntime().exec( "cat /dev/" + _arguments[ 1 ] );
				BufferedReader reader = reader = new BufferedReader( new InputStreamReader( process.getInputStream() ) );
				
				int counter = 0;
				while( counter < WAIT_TIME )
				{
					// collect the data
					String content = "";
					while( content.length() < SIZE )
					{
						String line = reader.readLine();
						if( line != null )
						{
							content = content + line;
						}
					}
					content = content.replaceAll( "\\s+", "" );

					Matcher matcher = Pattern.compile( _pattern ).matcher( content );
					if( matcher.find() )
					{
						if( matcher.group( 1 ).equals( tokens[ 0 ] ) )
						{
							String[] data = matcher.group( 2 ).split( "," );

							result = Double.parseDouble( data[ Integer.parseInt( tokens[ 1 ] ) ] );
							break;
						}
					}
					else
					{
						counter++;
					}
				}

				if( reader != null )
				{
					reader.close();
				}

				if( process != null )
				{
					process.destroy();
				}
			}
		}
		catch( IOException e )
		{
			_logger.log( Level.WARNING, "", e );
		}
		return result;
	}

	/**
	 * return the type and the index of the specified port
	 *
	 * @param   port    the specified port
	 * @return  the type and the index of the specified port
	 *
	 * @author  snowfoxy
	 * @since   2011-11-22
	 */
	private String[] splitPort( final String port )
	{
		String[] tokens = port.split( Gateway.DELIMITER );
		try
		{
			if( tokens.length == 2 )
			{
				if( tokens[ 0 ].substring( 2, 4 ).equals( _arguments[ 2 ] ) )
				{
					int type = Integer.parseInt( tokens[ 0 ].substring( 4 ) );
					int index = Integer.parseInt( tokens[ 1 ] );
					if( type >= MIN_TYPE && type <= MAX_TYPE && index < NUM_PORT )
					{
						return tokens;
					}
				}
			}
		}
		catch( IllegalArgumentException e )
		{
			_logger.log( Level.WARNING, "", e );
		}
		return null;
	}

	/**
	 * true if updating the specified port with the specified sensitivity successful
	 *
	 * @param   port		the specified port
	 * @param   sensitivity the specified sensitivity
	 * @return  true if modifying the specified port with the specified sensitivity successful
	 *
	 * @author  snowfoxy
	 * @since   2011-11-22
	 */
	protected boolean updatePort( final String port, final String sensitivity )
	{
		return true;
	}

	/**
	 * return the status of the write command with the specified port
	 *
	 * @param	port	the specified port
	 * @return	the status of the write command with the specified port
	 *
	 * @author	snowfoxy
	 * @since	2011-11-22
	 */
	protected Command.Message write( final String port )
	{
		return Command.Message.BAD_COMMAND;
	}
}
